package calvin.study.redis.utils;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.IdUtil;


/**
 * Redis工具类
 * 数据类型基本操作
 * @author wh
 */
@Component
public class RedisUtil {
	
	@Autowired
	@Qualifier("redisTemplate")
	private RedisTemplate<String,Object> redisTemplate;
	
	/**
	 * 指定缓存过期时间
	 * @Title: expire   
	 * @Description: TODO(描述这个方法的作用)
	 * @date: 2022年1月24日 下午3:04:23   
	 * @version:
	 * @param key 
	 * @param time 过期时间（秒）
	 * @return          
	 * @throws
	 */
	public boolean expire(String key,long time) {
		try {
			if(time > 0) {
				redisTemplate.expire(key, time, TimeUnit.SECONDS);
				return true;
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return false;
	}
	
	/**
	 * 获取过期时间
	 * @Title: getExpire   
	 * @Description: TODO(描述这个方法的作用)
	 * @date: 2022年1月24日 下午3:12:50   
	 * @version:
	 * @param key
	 * @return   key不存在返回-2       
	 * @throws
	 */
	public long getExpire(String key) {
		try {
			return redisTemplate.getExpire(key,TimeUnit.SECONDS);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return 0;
	}
	
	/**
	 * 判断Key是否存在
	 * @Title: hasKey   
	 * @Description: TODO(描述这个方法的作用)
	 * @date: 2022年1月24日 下午3:14:25   
	 * @version:
	 * @param key
	 * @return          
	 * @throws
	 */
	public boolean hasKey(String key) {
		try {
			return redisTemplate.hasKey(key);
		}catch (Exception e) {
			e.printStackTrace();
			return false;
		}
	}
	
	/**
	 * 删除缓存
	 * @Title: del   
	 * @Description: TODO(描述这个方法的作用)
	 * @date: 2022年1月24日 下午3:20:13   
	 * @version:
	 * @param key 一个或多个          
	 * @throws
	 */
	@SuppressWarnings("unchecked")
	public void del(String...key) {
		if(null!=key && key.length > 0) {
			if(key.length == 1) {
				redisTemplate.delete(key[0]);
			}else {
				redisTemplate.delete(CollectionUtils.arrayToList(key));
			}
		}
	}
	
	
	public Object get(String key) {
		return StringUtils.isBlank(key) ? null : redisTemplate.opsForValue().get(key);
	}
	
	public boolean set(String key,Object value) {
		try {
			redisTemplate.opsForValue().set(key, value);
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}
		return true;
	}
	
	public boolean set(String key,Object value,long time) {
		try {
			if(time > 0) {
				redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
			}else {
				set(key, value);
			}
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}
		return true;
	}
	
	/**
	 * 
	 * @Title: incr   
	 * @Description: TODO(描述这个方法的作用)
	 * @date: 2022年1月24日 下午3:27:59   
	 * @version:
	 * @param key
	 * @param delta 递增数
	 * @return 返回递增后的结果     
	 * @throws
	 */
	public long incr(String key, long delta) {
		if (delta < 0) {
			throw new RuntimeException("递增数必须大于0");
		}
		return redisTemplate.opsForValue().increment(key, delta);
	}
	
	/**
	 * 
	 * @Title: decr   
	 * @Description: TODO(描述这个方法的作用)
	 * @date: 2022年1月24日 下午3:30:24   
	 * @version:
	 * @param key
	 * @param delta
	 * @return          
	 * @throws
	 */
	public long decr(String key,long delta) {
		if (delta < 0) {
			throw new RuntimeException("递增数必须大于0");
		}
		return redisTemplate.opsForValue().decrement(key, delta);
	}

/////////////////////////////////////////////////////////////【HASH】/////////////////////////////////////////////////////////
	public Object hGet(String key, String hashKey) {
		return this.redisTemplate.opsForHash().get(key, hashKey);
	}
	
	/**
	 * 获取hash的键值，并转换为bean
	 *
	 * @param key           hash key
	 * @param cls          需要将数据转化的bean
	 * @return 返回bean
	 */
	public <T> T hGetToBean(String key,Class<T> cls) {
		T t = null;
		try {
			if(StringUtils.isBlank(key)) {
				return t;
			}
			BoundHashOperations<String, String, Object> boundHashOperations = this.redisTemplate.boundHashOps(key);
			//获取该hash中的所有键值
			Map<String, Object> res = boundHashOperations.entries();
			//转换为指定的bean
			t = BeanUtil.fillBeanWithMap(res, cls.newInstance(), false);
			//boundHashOperations.entries().forEach((m,n) -> System.out.println("获取map键值对:" + m + "-" + n));
		} catch (Exception e) {
			e.printStackTrace();
		}
		return t;
	}
	
	
	/**
	 * 按照key的生成规则，获取该规则下所有hashkey的数据
	 *
	 * @param cls          需要将数据转化的bean
	 * @return 返回集合<Bean>
	 */
	public <T> List<T> hGetList(String key,Class<T> cls) {
		List<T> list = new ArrayList<T>();
		// 获取该规则下所有的key
		Set<String> set = redisTemplate.keys(key);
		Iterator<String> keys = set.iterator();
		while (keys.hasNext()) {
			T t = hGetToBean(keys.next(), cls);
			list.add(t);
		}
		return list;
	}

	/**
	 * @Title: hmget   
	 * @Description: 获取hashkey对应的所有键值
	 * @date: 2022年1月24日 下午3:32:28   
	 * @version:
	 * @param key
	 * @return          
	 * @throws
	 */
	public Map<Object,Object> hmGet(String key){
		return this.redisTemplate.opsForHash().entries(key);
	}
	
	/**
	 * 
	 * @Title: hmset   
	 * @Description: TODO(描述这个方法的作用)
	 * @date: 2022年1月24日 下午3:33:21   
	 * @version:
	 * @param key 键
	 * @param map 对应多个值
	 * @return          
	 * @throws
	 */
	public boolean hmSet(String key,Map<String,Object> map) {
		try {
			this.redisTemplate.opsForHash().putAll(key, map);
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}
		return true;
	}
	
	public boolean hmSet(String key,Map<String,Object> map,long time) {
		try {
			hmSet(key, map);
			if(time > 0) {
				expire(key, time);
			}
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}
		return true;
	}
	
	/**
	 * 
	 * @Title: hset   
	 * @Description: 向一张hash表中插入数据
	 * @date: 2022年1月24日 下午3:38:01   
	 * @version:
	 * @param key
	 * @param item
	 * @param value
	 * @return          
	 * @throws
	 */
	public boolean hSet(String key,String hashKey,Object value) {
		try {
			this.redisTemplate.opsForHash().put(key, hashKey, value);
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}
		return true;
	}
	
	public boolean hSet(String key,String hashKey,Object value,long time) {
		try {
			hSet(key, hashKey, value);
			if(time > 0 ) {
				expire(key, time);
			}
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}
		return true;
	}
	
	public void hDel(String key,Object... hashKeys) {
		this.redisTemplate.opsForHash().delete(key, hashKeys);
	}
	
	public boolean hHasKey(String key,String hashKey) {
		return this.redisTemplate.opsForHash().hasKey(key, hashKey);
	}
	
	/**
	 * 
	 * @Title: hincr   
	 * @Description: TODO(描述这个方法的作用)
	 * @date: 2022年1月24日 下午3:51:52   
	 * @version:
	 * @param key
	 * @param item
	 * @param by 要增加几(大于0)
	 * @return          
	 * @throws
	 */
	public double hIncr(String key,String item,double by) {
		return this.redisTemplate.opsForHash().increment(key, item, by);
	}
	
	public double hDecr(String key,String item,double by) {
		return this.redisTemplate.opsForHash().increment(key, item, -by);
	}
/////////////////////////////////////////////////////////////【HASH】 end/////////////////////////////////////////////////////////	
	
	/**
	 * 获取该set下的所有值
	 * @param key
	 * @return
	 */
	public Set<Object> sGet(String key){
		try {
			return this.redisTemplate.opsForSet().members(key);
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}
	
	
	public boolean sIsMember(String key,Object value) {
		try {
			return this.redisTemplate.opsForSet().isMember(key, value);
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}
	}
	
	public long sAdd(String key,Object... values) {
		try {
			return this.redisTemplate.opsForSet().add(key, values);
		} catch (Exception e) {
			e.printStackTrace();
			return 0;
		}
	}
	
	public long sAddAndTime(String key, long time, Object... values) {
		try {
			Long count = sAdd(key, values);
			if (time > 0) {
				expire(key, time);
			}
			return count;
		} catch (Exception e) {
			e.printStackTrace();
			return 0;
		}
	}
	
	public long sGetSize(String key) {
		try {
			return this.redisTemplate.opsForSet().size(key);
		} catch (Exception e) {
			e.printStackTrace();
			return 0;
		}
	}
	
	public long setRemove(String key,Object...values) {
		try {
			return this.redisTemplate.opsForSet().remove(key, values);
		} catch (Exception e) {
			e.printStackTrace();
			return 0;
		}
	}
	
	// ===============================list=================================

    /**
     * 获取list缓存的内容
     *
     * @param key   键
     * @param start 开始
     * @param end   结束 0 到 -1代表所有值
     */
    public List<Object> lGet(String key, long start, long end) {
        try {
            return redisTemplate.opsForList().range(key, start, end);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }


    /**
     * 获取list缓存的长度
     *
     * @param key 键
     */
    public long lGetListSize(String key) {
        try {
            return redisTemplate.opsForList().size(key);
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }


    /**
     * 通过索引 获取list中的值
     *
     * @param key   键
     * @param index 索引 index>=0时， 0 表头，1 第二个元素，依次类推；index<0时，-1，表尾，-2倒数第二个元素，依次类推
     */
    public Object lGetIndex(String key, long index) {
        try {
            return redisTemplate.opsForList().index(key, index);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }


    /**
     * 将list放入缓存
     *
     * @param key   键
     * @param value 值
     */
    public boolean lSet(String key, Object value) {
        try {
            redisTemplate.opsForList().rightPush(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }


    /**
     * 将list放入缓存
     * @param key   键
     * @param value 值
     * @param time  时间(秒)
     */
    public boolean lSet(String key, Object value, long time) {
        try {
            redisTemplate.opsForList().rightPush(key, value);
            if (time > 0)
                expire(key, time);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }

    }


    /**
     * 将list放入缓存
     *
     * @param key   键
     * @param value 值
     * @return
     */
    public boolean lSet(String key, List<Object> value) {
        try {
            redisTemplate.opsForList().rightPushAll(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }

    }


    /**
     * 将list放入缓存
     *
     * @param key   键
     * @param value 值
     * @param time  时间(秒)
     * @return
     */
    public boolean lSet(String key, List<Object> value, long time) {
        try {
            redisTemplate.opsForList().rightPushAll(key, value);
            if (time > 0)
                expire(key, time);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }


    /**
     * 根据索引修改list中的某条数据
     *
     * @param key   键
     * @param index 索引
     * @param value 值
     * @return
     */

    public boolean lUpdateIndex(String key, long index, Object value) {
        try {
            redisTemplate.opsForList().set(key, index, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }


    /**
     * 移除N个值为value
     * @param key   键
     * @param count 移除多少个
     * @param value 值
     * @return 移除的个数
     */

    public long lRemove(String key, long count, Object value) {
        try {
            Long remove = redisTemplate.opsForList().remove(key, count, value);
            return remove;
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }
    
    /**
     * 弹出list的值
     * @param key 
     * @param flag 0从list最右边开始pop ， 1从list最左边开始pop
     * @return
     */
    public Object lPop(String key,Integer flag) {
    	return flag == 1 ? redisTemplate.opsForList().leftPop(key): redisTemplate.opsForList().rightPop(key);
    }
    
    /**
     * @Title: getSeqId   
     * @Description: 从redis中获取序列值，如果该序列不存在则自动生成
     * @date: 2022年1月25日 下午5:52:12   
     * @version:
     * @return          
     * @throws
     */
	public String getSeqId() {
		String seq = null;
		// 1.查询该序列下的数据是否存在
		// 2.不存在则生成序列
		// 3.如果序列存在，则从list集合中pop中一个值
		long seqLength = this.sGetSize("sequence");
		if (seqLength > 0) {
			List<Object>  list = redisTemplate.opsForSet().pop("sequence", 1);
			seq = list.get(0).toString();
		} else {
			// 生产10000个不重复的序列值
			for (int i = 0; i < 10000; i++) {
				String seqId = IdUtil.getSnowflake(1, 1).nextIdStr();
				this.sAdd("sequence", seqId);
			}
			List<Object> list = redisTemplate.opsForSet().pop("sequence", 1);
			seq = list.get(0).toString();
		}
		return seq;
	}

}
